package org.openbpm.bus.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.openbpm.base.api.exception.BusinessException;
import org.openbpm.base.core.executor.ExecutorFactory;
import org.openbpm.bus.api.constant.BusTableRelType;
import org.openbpm.bus.api.model.IBusinessData;
import org.openbpm.bus.api.model.IBusinessObject;
import org.openbpm.bus.api.model.IBusinessPermission;
import org.openbpm.bus.api.service.IBusinessDataService;
import org.openbpm.bus.executor.assemblyval.AssemblyValExecuteChain;
import org.openbpm.bus.executor.assemblyval.AssemblyValParam;
import org.openbpm.bus.executor.parseval.ParseValExecuteChain;
import org.openbpm.bus.executor.parseval.ParseValParam;
import org.openbpm.bus.manager.BusinessObjectManager;
import org.openbpm.bus.model.BusTableRel;
import org.openbpm.bus.model.BusinessColumn;
import org.openbpm.bus.model.BusinessData;
import org.openbpm.bus.model.BusinessObject;
import org.openbpm.bus.service.BusinessDataPersistenceServiceFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class BusinessDataService
        implements IBusinessDataService {
    @Autowired
    BusinessObjectManager businessObjectManager;

    public JSONObject saveFormDefData(JSONObject data, IBusinessPermission businessPermission) {
        JSONObject result = new JSONObject();
        for (Map.Entry<String, Object> entry : (Iterable<Map.Entry<String, Object>>) data.entrySet()) {
            String boKey = entry.getKey();
            JSONObject boData = (JSONObject) entry.getValue();
            BusinessData businessData = (BusinessData) parseBusinessData(boData, boKey);
            businessData.getBusTableRel().getBusObj().setPermission(businessPermission.getBusObj(boKey));
            businessData = BusinessDataPersistenceServiceFactory.saveData(businessData);
            // add data to result
            result.put(boKey, businessData);
        }
        return result;
    }


    public JSONObject getFormDefData(IBusinessObject businessObject, Object id) {
        BusinessData businessData = (BusinessData) loadData(businessObject, id);
        JSONObject data = new JSONObject();

        assemblyFormDefData(data, (IBusinessData) businessData);
        return data;
    }


    private void initFormDefData(BusinessData businessData) {
        BusTableRel busTableRel = businessData.getBusTableRel();
        if(busTableRel!=null){
            if (businessData.getPk() == null) {
                businessData.setDbData(busTableRel.getTable().initDbData());
            }
            for (BusTableRel rel : busTableRel.getChildren()) {
                if (!BusTableRelType.ONE_TO_ONE.equalsWithKey(rel.getType()) ||
                        !businessData.getChild(rel.getTableKey()).isEmpty()) {
                    continue;
                }
                BusinessData childData = new BusinessData();
                childData.setBusTableRel(rel);
                initFormDefData(childData);
                businessData.addChildren(childData);
            }
        }
    }


    public JSONObject assemblyFormDefData(IBusinessData businessData) {
        JSONObject data = new JSONObject();
        assemblyFormDefData(data, businessData);
        return data;
    }


    private void assemblyFormDefData(JSONObject data, IBusinessData ibusinessData) {
        BusinessData businessData = (BusinessData) ibusinessData;

        for (Map.Entry<String, Object> entry : (Iterable<Map.Entry<String, Object>>) businessData.getData().entrySet()) {
            BusinessColumn column = businessData.getBusTableRel().getTable().getColumnByKey(entry.getKey());
            if (column == null) {
                throw new BusinessException("businessData中出现了非业务对象的字段，请核查：" + (String) entry.getKey());
            }
            AssemblyValParam param = new AssemblyValParam(data, column, entry.getValue());
            ExecutorFactory.execute(AssemblyValExecuteChain.class, param);
        }


        for (Map.Entry<String, List<BusinessData>> entry : (Iterable<Map.Entry<String, List<BusinessData>>>) businessData.getChildren().entrySet()) {
            String tableKey = entry.getKey();
            List<BusinessData> children = entry.getValue();
            if (BusTableRelType.ONE_TO_ONE.equalsWithKey(((BusinessData) children.get(0)).getBusTableRel().getType())) {
                JSONObject cData = new JSONObject();
                if (!children.isEmpty()) {
                    cData = new JSONObject(((BusinessData) children.get(0)).getData());
                }
                assemblyFormDefData(cData, (IBusinessData) children.get(0));
                data.put(tableKey, cData);
                continue;
            }
            JSONArray dataList = new JSONArray();
            for (BusinessData bd : children) {
                JSONObject cData = new JSONObject(bd.getData());
                assemblyFormDefData(cData, (IBusinessData) bd);
                dataList.add(cData);
            }
            data.put(tableKey + "List", dataList);
        }
    }


    private BusinessData analysisFormDefData(Object object, BusTableRel relation) {
        BusinessData businessData = new BusinessData();
        businessData.setBusTableRel(relation);

        if (object instanceof JSONObject) {
            JSONObject jsonObject = (JSONObject) object;
            Map<String, Object> bdData = new HashMap<>();
            for (Map.Entry<String, Object> enty : (Iterable<Map.Entry<String, Object>>) jsonObject.entrySet()) {

                if (!(enty.getValue() instanceof JSONArray) && !(enty.getValue() instanceof JSONObject)) {
                    ParseValParam param = new ParseValParam(enty.getKey(), enty.getValue(), bdData, relation);
                    ExecutorFactory.execute(ParseValExecuteChain.class, param);
                }

                if (enty.getValue() instanceof JSONArray) {
                    String tableKey = ((String) enty.getKey()).substring(0, ((String) enty.getKey()).length() - 4);
                    BusTableRel rel = relation.find(tableKey);
                    if(rel!=null){
                        for (Object obj : (JSONArray) enty.getValue()) {
                            businessData.addChildren(analysisFormDefData(obj, rel));
                        }
                    }
                }

                if (enty.getValue() instanceof JSONObject) {
                    BusTableRel rel = relation.find(enty.getKey());
                    if(rel!=null){
                        businessData.addChildren(analysisFormDefData(enty.getValue(), rel));
                    }
                }
            }
            businessData.setData(bdData);
        }
        return businessData;
    }


    public void removeData(IBusinessObject businessObject, Object id) {
        BusinessDataPersistenceServiceFactory.removeData((BusinessObject) businessObject, id);
    }


    public IBusinessData saveData(IBusinessData businessData) {
        return BusinessDataPersistenceServiceFactory.saveData((BusinessData) businessData);
    }


    public IBusinessData loadData(IBusinessObject businessObject, Object id) {
        return loadData(businessObject, id, true);
    }


    public IBusinessData loadData(IBusinessObject businessObject, Object id, boolean init) {
        BusinessData businessData = BusinessDataPersistenceServiceFactory.loadData((BusinessObject) businessObject, id);
        if (init) {
            initFormDefData(businessData);
        }

        return (IBusinessData) businessData;
    }


    public IBusinessData parseBusinessData(JSONObject jsonObject, String boKey) {
        BusinessObject businessObject = this.businessObjectManager.getFilledByKey(boKey);
        return (IBusinessData) analysisFormDefData(jsonObject, businessObject.getRelation());
    }
}
